﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;

namespace MiaProject
{
    class Actor
    {
        public String name;
        public bool status;
        public Vector3 pos;
        public float scaleTiro;        
        public float rotation;
        public float rotationStart;
        public float scale;
        public float scaleCollision;
        public Model model;
        public int life;
        public int powerAtack;
        public int powerShield;        
        //tiro
        public Model tiro;
        public String soundTiro;
        public String soundTiroCollision;
        public String soundDead;
        public List<Tiro> tiros;
        protected float timeTiro;
        public float tiroFrequence;
        public int distTargetAtack;
        public Vector3 distTiroPosStart;



        //steering
        protected Steering steering;
        protected Vector3 steeringForce;
        protected Vector3 dir;
        protected Vector3 vel;
        public int speed;
        protected float maxVel;
        protected double massa;


        protected Actor()
        {
            pos = Vector3.Zero;
            this.tiros = new List<Tiro>();
            this.timeTiro = 0;
            this.tiroFrequence = 1;
            this.distTargetAtack = 500;
            this.status = true;
            this.rotationStart = 0.0f;
            this.scaleCollision = 1.0f;
            this.distTiroPosStart = new Vector3(1.0f,0.0f,1.0f);//multiplica x e z. y somente soma (setTiro)
            this.life = 0;
            this.speed = 45;
            this.scaleTiro = 1.0f;
            
            this.powerAtack = 0;
            this.powerShield = 0;
        }
        protected void initSteering()
        {
            this.steering = new Steering();
            this.steeringForce = Vector3.Zero;
            this.dir = new Vector3(0, 0, 1);
            this.maxVel = 2;
            this.massa = 1;
            this.vel = Vector3.Zero;
        }
        protected void updateSteering(GameTime gameTime, Vector3 targetPos)
        {
            float interval = (float)gameTime.ElapsedGameTime.TotalSeconds;

            this.steeringForce = this.steering.calculateSteering(this.pos, this.vel, this.maxVel, targetPos, 1, interval);

            Vector3 acceleration = Vector3.Zero;
            acceleration.X = (float)(this.steeringForce.X / massa);
            acceleration.Y = (float)(this.steeringForce.Y / massa);
            acceleration.Z = (float)(this.steeringForce.Z / massa);
            //Vector3.Divide
            //update velocity
            this.vel += (acceleration * interval);

            //make sure vehicle does not exceed maximum velocity


            this.vel = truncate(this.vel,this.maxVel);

            float speed = this.speed * interval;
            //if (speed > 1.0f) speed = 1.0f;

            this.pos.X = this.pos.X + (this.vel.X * speed);
            this.pos.Z = this.pos.Z + (this.vel.Z * speed);
   
        }

        protected int side(Vector3 u, Vector3 v)
        {
            if (u.Z * v.Z > u.X * v.Z)
                return -1;
            else
                return 1;
        }
        private Vector3 truncate(Vector3 v, float max)
        {
            if (v.LengthSquared() > max)
            {
                v.Normalize();
                v *= max;
            }
            return v;

        }
        protected void drawActor(Matrix view, Matrix projection, BoundingFrustum frustum)
        {
            if (!this.status)
                return;
            //GraphicsDevice device = GraphicsDevice;
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);


            // Cria um ContainmentType, de tipo Disjoint
            ContainmentType currentContainmentType = ContainmentType.Disjoint;

            // Pega a esfera do objeto…
            BoundingSphere meshBoundingSphere = new BoundingSphere(pos, model.Meshes[0].BoundingSphere.Radius * this.scaleCollision);
                
                
            //…e testa com o Frustum da Camera
            currentContainmentType = frustum.Contains(meshBoundingSphere);
            //Se a esfera estiver dentro do frustum…
            if (currentContainmentType != ContainmentType.Disjoint)
            {
                // …renderizar
                /*
                 Desenhar cada um dos meshes do modelo e aplicar os efeitos
                 * Cada modelo pode ter mais de uma mesh e a cada mesh podem ser aplicados 1 ou mais efeitos;

                 */
                foreach (ModelMesh mesh in model.Meshes)
                {
                    foreach (BasicEffect effect in mesh.Effects)
                    {
                        effect.EnableDefaultLighting();//habilita luz


                        //Posição do modelo no mundo;Coloquei uma rotação em Y, para fazer o modelo girar em relação a este eixo;
                        effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateScale(scale) * Matrix.CreateRotationY(rotationStart + rotation) * Matrix.CreateTranslation(pos);

                        //Posição e direção da câmera;
                        effect.View = view;

                        //Controla como o mundo 3D é projetado de maneira 2D;
                        effect.Projection = projection;
                    }
                    mesh.Draw();
                }
            }
        }//end of method

        protected void drawTiroScale(Matrix view, Matrix projection,float scale)
        {
            Matrix[] transformsTiro;

            foreach (Tiro t in this.tiros)
            {
                if (!t.status)
                    continue;


                transformsTiro = new Matrix[this.tiro.Bones.Count];
                this.tiro.CopyAbsoluteBoneTransformsTo(transformsTiro);

                foreach (ModelMesh mesh in this.tiro.Meshes)
                {
                    foreach (BasicEffect effect in mesh.Effects)
                    {
                        effect.EnableDefaultLighting();//habilita luz

                        //Posição do modelo no mundo;Coloquei uma rotação em Y, para fazer o modelo girar em relação a este eixo;
                        effect.World = transformsTiro[mesh.ParentBone.Index] * Matrix.CreateScale(4.0f*scale) * Matrix.CreateRotationY(t.modelRot) * Matrix.CreateTranslation(t.pos);

                        //Posição e direção da câmera;
                        effect.View = view;

                        //Controla como o mundo 3D é projetado de maneira 2D;
                        effect.Projection = projection;

                    }
                    mesh.Draw();
                }

            }
        }
        protected void drawTiro(Matrix view, Matrix projection)
        {
            this.drawTiroScale(view, projection, 1.0f);
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private void setTiro(Vector3 posTarget)
        {
            if (!this.status)
                return;
            
            //TIRO 1
            Tiro t = new Tiro();

            Vector3 dir = posTarget - pos;
            dir.Normalize();
            Vector3 posStart = this.pos;
            posStart.X += dir.X * distTiroPosStart.X;
            posStart.Y += distTiroPosStart.Y;
            posStart.Z += dir.Z * distTiroPosStart.Z;
            t.startTiro(posStart, dir, this.rotation + 3.1f);

            tiros.Add(t);

            Sound s = Sound.getInstance();
            s.soundBank.PlayCue(this.soundTiro);            
        }//end method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        protected void updateTiro(GameTime gameTime,Vector3 posTarget)
        {
            Vector3 v = posTarget - this.pos;
            float d = v.Length();
            if (v.Length() < distTargetAtack)
            {
                //verificar time
                if ((float)gameTime.TotalGameTime.TotalSeconds - this.timeTiro > tiroFrequence)
                {
                    this.timeTiro = (float)gameTime.TotalGameTime.TotalSeconds;
                    this.setTiro(posTarget);
                }
            }
            
            
            this.moveTiro(gameTime);
            this.deleteTiro();

        }
        private void moveTiro(GameTime gameTime)
        {
            foreach (Tiro t in tiros)
                t.move((float)gameTime.ElapsedGameTime.TotalSeconds);

        }//end method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        private void deleteTiro()
        {
            List<Tiro> deleteTiro = new List<Tiro>();
            foreach (Tiro t in tiros)
            {
                if (!t.status)
                    deleteTiro.Add(t);
            }
            foreach (Tiro t in deleteTiro)
                tiros.Remove(t);
        }//end method
       

    }
}
